part of '../time.dart';

/// 代表第n年第n周
/// 规定一年的第一周为元旦所在的一周，如果之前有去年的日期，那么所有日期都归于今年
class Week {
  int year;
  int weekNum;

  Week(this.year, this.weekNum) {
    // 周数不能大于当年的所有周数
    if (weekNum > weeksOnYear(year)) {
      throw Bug("$year年共有${weeksOnYear(year)}周，但是输入了$weekNum周，超出范围");
    }
    if (weekNum < 0) throw Bug("不可能有0或负周");

    // 如果本周刚好为今年的最后一周，并且两年之间有重叠，那么直接变为来年的第一周
    if (weekNum == weeksOnYear(year) && Date(year + 1, 1, 1).weekday != 1) {
      year++;
      weekNum = 1;
    }
  }

  factory Week.now() {
    // 获取今天的日期，并计算它属于第几周
    final today = Date.now();
    // 将年份和周数构建为Week返回
    return Week(today.year, today.weekNumber);
  }

  factory Week.fromDate(Date date) => Week(date.year, date.weekNumber);

  /// 有时候一周会横跨2个月，因此使用List来表示
  List<int> get months {
    // 获取本周 第一天和最后一天的月份
    int firstMonth = dates.first.month;
    int lastMonth = dates.last.month;
    // 如果月份不同，则返回2个的List
    if (firstMonth != lastMonth) {
      return [firstMonth, lastMonth];
    } else {
      // 如果月份相同，则返回1个数字的List
      return [firstMonth];
    }
  }

  /// 获取这一周所代表的7天
  List<Date> get dates {
    // 找到该年的第一天
    DateTime firstDayOfYear = DateTime(year, 1, 1);
    // 计算该年的第一天是星期几
    int dayOfWeek = firstDayOfYear.weekday;
    // 计算该周的起始日期
    DateTime startOfWeek =
        firstDayOfYear.add(Duration(days: (weekNum - 1) * 7 - (dayOfWeek - 1)));

    // 获取这一周的7个日期
    List<Date> dates = [];
    for (int i = 0; i < 7; i++) {
      DateTime date = startOfWeek.add(Duration(days: i));
      dates.add(Date(date.year, date.month, date.day));
    }
    return dates;
  }

  /// 往前、往后推移n周，如果遇到变成下一年了，那么年也会有所改变
  Week operator +(int n) {
    // n有可能是任意数字，先对年份进行粗调，直到n介于0-53之间
    // int cumulate = n;
    // Week temporaryWeek = this;
    // // 当累计数小于0时，将年份往前
    // while (cumulate < 0) {
    //   // 首先检查当前是第几周，如果不是第一周的话，先将周数调整为1，看看累计数值能否为正
    //   if (temporaryWeek.weekNum > 1) {
    //     int diff = temporaryWeek - 1;
    //     cumulate += diff;
    //     temporaryWeek = Week(temporaryWeek.year, 1);
    //   } else {
    //     // 如果是第1周，那么就要以年为单位进行调整
    //     // 首先，直接计算去年有多少个星期，累加上去，并让临时日期减去一年
    //     int diff = weeksOnYear(temporaryWeek.year - 1);
    //     cumulate += diff;
    //     temporaryWeek = Week(temporaryWeek.year - 1, temporaryWeek.weekNum);
    //     // 如果去年的最后一天不是星期天，代表着最后一天和来年的第一天有重叠，这种情况下需要多减去1周
    //     if ((Date(temporaryWeek.year + 1, 1, 1) - 1).weekday != 7) {
    //       cumulate--;
    //     }
    //   }
    // }

    // // 再长的一年，也不可能加53个星期，因此只要大于等于53，就意味着需要往前移
    // while (cumulate >= 53) {
    //   // 首先如果日期不是第一周，则先将日期累加上去，然后以整年为单位进行变化
    //   if (temporaryWeek.weekNum > 1) {
    //     int diff = temporaryWeek.weekNum - 1;
    //     cumulate += diff;
    //     temporaryWeek = Week(temporaryWeek.year, 1);
    //   } else {
    //     // 如果是第一周，那么以整年为单位进行调整
    //     int diff = weeksOnYear(temporaryWeek.year + 1);
    //     cumulate -= diff;
    //     temporaryWeek = Week(temporaryWeek.year + 1, temporaryWeek.weekNum);
    //     // 当涉及到跨年时，如果今年的最后一天不是星期天，那么年末和来年初是有重叠的，这种情况下需要将cumulate多加一周以抵消重叠
    //     if ((Date(temporaryWeek.year + 1, 1, 1) - 1).weekday != 7) {
    //       cumulate++;
    //     }
    //   }
    // }

    // // 经过调整后，n应该是0-52的一个数字，那么此时再排除一种情况：
    // // 比如今年只有52周，但是n是52，则直接返回明年的第一周
    // if (weeksOnYear(temporaryWeek.year) == 52 && cumulate == 52) {
    //   return Week(temporaryWeek.year + 1, 1);
    // }

    // 如果不是这个情况的话，都可以直接做计算
    // return Week(temporaryWeek.year, cumulate + 1);

    // 提取Week的年份、星期数，并将它添加n
    int tempYear = year;
    int tempWeek = weekNum + n;

    // 如果添加后的星期数小于等于0，则意味着需要往前平移
    while (tempWeek <= 0) {
      // 将年份回退一年，并计算上一年有多少个星期
      tempYear--;
      // 将星期数兑现到周数中
      tempWeek += weeksOnYear(tempYear);
      // 如果当年的最后一天不是星期天，则意味着两年交汇处有重叠，此时对week做一个-1的修正
      if ((Date(tempYear, 1, 1) - 1).weekNumber != 7) {
        tempWeek--;
      }
    }

    // 如果添加后的星期数超过了当年最大星期数，则意味着年需要累加
    while (tempWeek > weeksOnYear(tempYear)) {
      // 将星期数减少对应的周数
      tempWeek -= weeksOnYear(tempYear);
      // 将年份累加
      tempYear++;
      // 如果来年的第一天不是周一，则意味着两年交汇处有重叠，此时对week需要做一个+1的修正
      if (Date(tempYear, 1, 1).weekday != 1) {
        tempWeek++;
      }
    }

    // 将计算好的日期返回
    return Week(tempYear, tempWeek);
  }

  /// 比如"2024年 第44周(10-11月)"
  @override
  String toString() {
    String monthStr = months.first.toString();
    if (months.length > 1) {
      monthStr += "-${months.last}";
    }
    // 确定当前周的文字内容
    return "$year年 第$weekNum周($monthStr月)";
  }

  /// 对n取负后传入函数即可
  Week operator -(int n) => this + (-n);

  @override
  bool operator ==(Object other) {
    if (other is Week) {
      return other.year == year && other.weekNum == weekNum;
    } else {
      return false;
    }
  }

  @override
  int get hashCode => Object.hash(year, weekNum);
}

int weeksOnYear(int year) {
  // 先获取一年的最后一天
  Date lastDate = Date(year + 1, 1, 1) - 1;
  // 获取最后一天的星期数，并返回
  return lastDate.weekNumber;
}
